function Item(o) {
	this.key = o.key;
	this.start_date = DateUtils.parse(Item.dateFormat, this.starts().attr('value'));
	this.end_date = DateUtils.parse(Item.dateFormat, this.ends().attr('value'));
	this.children = o.children;
	this.parent_key = o.parent;
}

Item.baseId = "";
Item.items = undefined;
Item.dateFormat = 'dd/mm/yy';
Item.register = function(item) {
	Item.items[item.key] = item;
};

Item.init = function(baseId, dateFormat, arr, roots) {
	Item.items = new Array();
	Item.roots = roots;
	Item.baseId = baseId;
	Item.dateFormat = dateFormat;
	for (var i in arr)
	{
		var item = new Item(arr[i]);
		Item.register(item);
		item.build_box();
		item.bind();
	}
	Item.customizeCheckboxes();
};

Item.find = function(id) {
	return Item.items[id];
};

Item.masterRoot = function(){
	for (v in Item.roots) {
		var item = Item.find(Item.roots[v]);
		if (item.checkbox().attr('value') == '1') {
			return item;
		}
	}
};

Item.customizeCheckboxes = function() {
	var root = false;
	for (v in Item.roots) {
		var item = Item.find(Item.roots[v]);
		if (item.checkbox().attr('value') == '1' && !root) {
			item.starts().removeAttr('disabled');
			root = true;
		} else {
			item.starts().attr('disabled', 'true');
		}
		item.ends().attr('disabled', 'true');
	}
};

Item.prototype.build_box = function() {
	this.checkbox().each(function(){
		var id = this.id +"_img";
		var checked = (this.type == 'checkbox' && $(this).is(':checked')) || (this.type != 'checkbox' && this.value == '1');
		this.type = 'hidden';
		if(checked) {
			$(this).after('<div class="checkbox checked" id="' + id + '"><a class="cb_link"><img src="/images/empty.png" width="18" height="16" /></a></div>');
		} else {
			$(this).after('<div class="checkbox unchecked" id="' + id + '"><a class="cb_link"><img src="/images/empty.png" width="18" height="16" /></a></div>');
		}
	});
	this.user_inputs().each(function(){
		var id = this.id +"_img";
		$(this).children(":last").after('<a id="' + id + '" class="dlg_link"><img src="/images/comment.png" /></a>');
	});
	this.dialog().addClass('dialog').hide();
	this.validation_comment().hide();
	this.objective_comments().hide();
};

Item.prototype.checkbox = function() {
	var id = Item.baseId + this.key + "_checked";
	return $("#"+id);
};

Item.prototype.checkbox_image = function() {
	var id = Item.baseId + this.key + "_checked_img";
	return $("#"+id);
};

Item.prototype.dialog = function () {
	var id = "comment_"+this.key;
	return $("#"+id);
};

Item.prototype.user_inputs = function () {
	var id = "user_inputs_"+this.key;
	return $("#"+id);
};

Item.prototype.dialog_link = function () {
	var id = "user_inputs_"+this.key+"_img";
	return $("#"+id);
};

Item.prototype.starts = function() {
	var id = Item.baseId + this.key + "_start_date";
	return $("#"+id);
};

Item.prototype.ends = function() {
	var id = Item.baseId + this.key + "_end_date";
	return $("#"+id);
};

Item.prototype.adviser_validation = function() {
	var id = Item.baseId + this.key + "_adviser_validation";
	return $("#"+id);
};

Item.prototype.owner_validation = function() {
	var id = Item.baseId + this.key + "_owner_validation";
	return $("#"+id);
};

Item.prototype.objective = function() {
	var id = Item.baseId + this.key + "_objective";
	return $("#"+id);
};

Item.prototype.objective_comments = function() {
	var id = "objective_comments_" + this.key;
	return $("#"+id);
};

Item.prototype.validation_comment = function() {
	var id = "validation_comment_" + this.key;
	return $("#"+id);
};

Item.prototype.setStartDate = function (date) {
	if (this.checkbox().attr('value') == '0') {
		this.sav_start_date = date;
	} else {
		this.start_date=date;
	}
	this.starts().attr('value', DateUtils.format(Item.dateFormat, this.start_date));
};

Item.prototype.setEndDate = function (date) {
	if (this.checkbox().attr('value') == '0') {
		this.sav_end_date = date;
	} else {
		this.end_date=date;
	}
	this.ends().attr('value', DateUtils.format(Item.dateFormat, this.end_date));
};

Item.prototype.propagateEndDateToChildren = function (old, date) {
	// on met à jour la date de fin des enfants 
	// qui ont la meme date de fin que le noeud que l'on modifie
	var children = this.children.slice(0); 
	while (children.length > 0) {
		var child = Item.find(children.shift());
		if (child.end_date.getTime() == old.getTime() || child.end_date.getTime() >= date.getTime()) {
			child.checkEndDate(date);
		}
		children = children.concat(child.children);
	}
	
	children = this.children.slice(0); 
	while (children.length > 0) {
		var child = Item.find(children.shift());
		if (child.end_date.getTime() == old.getTime() || child.end_date.getTime() >= date.getTime()) {
			child.setEndDate(date);
		}
		children = children.concat(child.children);
	}
};

Item.prototype.isLastChildWithSameEndDate = function () {
	if (this.parent() == null || this.parent() == undefined) {
		return false;
	}
	
	var n = 0;
	for (var v in this.parent().children) {
		var child = Item.find(this.parent().children[v]);
		if (this != child && child.end_date.getTime() >= this.parent().end_date.getTime()) {
			n++;
		}
	}
	
	// si c'est le dernier, on met à jour
	return (n == 0);
};

Item.prototype.isLastChildWithSameStartDate = function () {
	if (this.parent() == null || this.parent() == undefined) {
		return false;
	}
	
	// on compte le nombre d'item qui ont la même date de début que le parent (sauf lui)
	var n = 0;
	for (var v in this.parent().children) {
		var child = Item.find(this.parent().children[v]);
		if (child == undefined || child == null) {
			console.error("Can't find item named " + this.parent().children[v]); 
		} else if (this != child && child.start_date.getTime() <= this.parent().start_date.getTime()) {
			n++;
		}
	}
	
	// si c'est le dernier, c'est pas bon
	return (n == 0); 
};	


Item.prototype.propagateEndDateToParent = function (old, date) {
	// on met à jour le parent si la nouvelle date de fin est postérieure
	var p = this.parent();
	while (p != null && p != undefined) {
		if (p.end_date.getTime() < date.getTime()) {
			p.setEndDate(date);
			p = p.parent();
		} else {
			p = undefined;
		}
	}
	
	var idx = Item.roots.indexOf(this.root().key);
	if (idx >= 0) {
		// on est sur une étape. On décalle donc tout le reste.
		// on compte le nombre de jour d'écart entre l'ancienne date de fin et la nouvelle
		var nb = DateUtils.daysDiff(date, old);
		
		if (nb != 0) {
			var stack = Item.roots.slice(idx+1);
			while (stack.length > 0) {
				var tmp = stack.shift();
				tmp = Item.find(tmp);
				tmp.setStartDate(DateUtils.add(tmp.start_date, DateUtils.DAY, nb));
				tmp.setEndDate(DateUtils.add(tmp.end_date, DateUtils.DAY, nb));
				stack = stack.concat(tmp.children);
			}
		}
	}
};

Item.prototype.unchecked = function() {
	this.checkbox_image().removeClass('checked undetermined').addClass('unchecked');
	if(this.checkbox().attr('value') == '0')
		return;
		
	var today = DateUtils.today(); 
	this.checkbox().attr('value', '0');
	this.sav_start_date = this.start_date;
	this.start_date = today;
	this.sav_end_date = this.end_date;
	this.end_date = today;
	this.starts().attr('value', DateUtils.format(Item.dateFormat, this.start_date)).attr('disabled', 'true');
	this.ends().attr('value', DateUtils.format(Item.dateFormat, this.end_date)).attr('disabled', 'true');
};

Item.prototype.checked = function() {
	this.checkbox_image().removeClass('unchecked undetermined').addClass('checked');
	if(this.checkbox().attr('value') == '1')
		return;
		
	this.checkbox().attr('value', '1');
	if (this.sav_start_date != undefined)
		this.start_date = this.sav_start_date;
	this.sav_start_date = undefined;
	if (this.sav_end_date != undefined)
		this.end_date = this.sav_end_date;
	this.sav_end_date = undefined;
	this.starts().attr('value', DateUtils.format(Item.dateFormat, this.start_date)).removeAttr('disabled');
	this.ends().attr('value', DateUtils.format(Item.dateFormat, this.end_date)).removeAttr('disabled');
};

Item.prototype.undetermined = function() {
	this.checkbox_image().removeClass('unchecked checked').addClass('undetermined');
	if(this.checkbox().attr('value') == '1')
		return;
		
	this.checkbox().attr('value', '1');
	
	if (this.sav_start_date != undefined)
		this.start_date = this.sav_start_date;
	this.sav_start_date = undefined;
	if (this.sav_end_date != undefined)
		this.end_date = this.sav_end_date;
	this.sav_end_date = undefined;
	this.starts().attr('value', DateUtils.format(Item.dateFormat, this.start_date)).removeAttr('disabled');
	this.ends().attr('value', DateUtils.format(Item.dateFormat, this.end_date)).removeAttr('disabled');
};

Item.prototype.bind = function() {
	var item = this;
	this.starts().change(function(event) {
		// si c'est le dernier, on simule un changement de date du parent
		if (item.isLastChildWithSameStartDate()) {
			item.parent().starts().attr('value', event.target.value).trigger('change');
			return;
		}
	
		var date = DateUtils.parse(Item.dateFormat, event.target.value);
		var old = item.start_date;
		
		try {
			item.checkStartDate(date);
			item.setStartDate(date);
				
			// on met à jour la date de début des enfants 
			// qui ont la meme date de début que le noeud que l'on modifie
			var children = item.children.slice(0); 
			var updated = new Array();
			while (children.length > 0) {
				var child = Item.find(children.shift());
				if (child.start_date.getTime() == old.getTime() || child.start_date.getTime() <= date.getTime()) {
					child.checkStartDate(date);
					updated.push(child);
				}
				children = children.concat(child.children);
			}
			
			for (var i in updated) {
				var child = updated[i];
				if (child.start_date.getTime() == old.getTime() || child.start_date.getTime() <= date.getTime()) {
					child.setStartDate(date);
				}
			}
			
		} catch (ex) {
			var message = StringUtils.trimToNull(ex.message);
			if (message == undefined) {
				message = 'La date de début ne peut pas être modifiée.';
			}
			
			alert(message);
			item.setStartDate(old);
			if (console != null && console != undefined) {
				console.error(message);
				console.error("Caused by: "+ex.stack);
			}
		}
	}).blur(function(event){
		this.value = DateUtils.format(Item.dateFormat, item.start_date);
	});
	
	this.ends().change(function(event){
		// si c'est le dernier, on simule un changement de date du parent
		if (item.isLastChildWithSameEndDate()) {
			item.parent().ends().attr('value', event.target.value).trigger('change');
			return;
		}
	
		var date = DateUtils.parse(Item.dateFormat, event.target.value);
		var old = item.end_date;
		try {
			item.checkEndDate(date);
			item.setEndDate(date);
			item.propagateEndDateToChildren(old, date);
			item.propagateEndDateToParent(old, date);
		} catch (ex) {
			var message = StringUtils.trimToNull(ex.message);
			if (message == undefined) {
				message = 'La date de début ne peut pas être modifiée.';
			}
			
			alert(message);
			item.setEndDate(old);
			if (console != null && console != undefined) {
				console.error(ex.message);
				console.error('Caused by '+ex.stack);
			}
		}
	});
	
	this.checkbox_image().children('a').bind('click', function(event) {
		var old = item.end_date;
		var old_root = item.root().end_date;
		if (item.checkbox_image().hasClass('checked')) {
			var value='0';
		} else {
			var value='1';
		}
		
		// on met a jour la date de début puis la date de fin
		var stack = new Array(item.key);
		while (stack.length > 0) {
			var i = stack.shift();
			try {
				var tmp = Item.find(i);
				if (value == '0') {
					tmp.unchecked();
				} else if (value == '1') {
					tmp.checked();
				}
				stack = stack.concat(tmp.children);
			} catch(ex){
				if (console != null && console != undefined) {
					console.error("Could not check/uncheck item " + i + ": " + ex.message);
					console.error(ex.stack);
				}
			}
		}
		
		var tmp = item.parent();
		while (tmp != undefined && tmp != null) {
			var cb = tmp.checkbox_image();
			var hasChecked = cb.parent().parent().find('.checkbox').not(cb).hasClass('checked');
			var hasUnchecked = cb.parent().parent().find('.checkbox').not(cb).hasClass('unchecked');
			var hasUndeterminated = cb.parent().parent().find('.checkbox').not(cb).hasClass('undetermined');
			
			if (hasUndeterminated) {
				tmp.undetermined();
			} else if (hasChecked && hasUnchecked) {
				tmp.undetermined();
			} else if (hasChecked) {
				tmp.checked();
			} else {
				tmp.unchecked();
			}
			
			tmp = tmp.parent();
		}
		
		item.root().propagateEndDateToParent(old_root, item.root().end_date);
		Item.customizeCheckboxes();
	});
	
	this.dialog_link().bind('click', function(event) {
		//item.dialog().dialog("open");
		item.dialog().slideToggle(); 
		return false;
	});
	
	this.objective().change(function(event){ item.toggle_objective(); });
	this.adviser_validation().change(function(event){ item.toggle_adviser_validation(); });
	this.owner_validation().change(function(event){ item.toggle_owner_validation(); });
};

Item.prototype.toggle_adviser_validation = function() {
	this.set_comment_icon();
	var adv_valid = this.adviser_validation().is(':checked');
	var own_valid = this.owner_validation().is(':checked');
	
	if (adv_valid || own_valid) {
		this.dialog().children(".validation").slideDown();		
	} else {
		this.dialog().children(".validation").slideUp();
	}
	
	if (adv_valid){
		this.adviser_validation().attr('value', 'y');
		var stack = this.children.slice(0);
		while (stack.length > 0) {
			var tmp = stack.shift();
			tmp = Item.find(tmp);
			if (!tmp.adviser_validation().is(':checked')) {
				tmp.adviser_validation().attr('value', 'i');
				tmp.adviser_validation().attr('checked', true).attr('readonly', true).attr('disabled', true);
				tmp.set_comment_icon();
				stack = stack.concat(tmp.children);
			}
		}
	} else {
		this.adviser_validation().attr('value', 'n');
		var stack = this.children.slice(0);
		while (stack.length > 0) {
			var tmp = stack.shift();
			tmp = Item.find(tmp);
			if (tmp.adviser_validation().is(':disabled')) {
				tmp.adviser_validation().attr('value', 'n');
				tmp.adviser_validation().removeAttr('checked').removeAttr('readonly').removeAttr('disabled');
				tmp.set_comment_icon();
				stack = stack.concat(tmp.children);
			}
		}
	}
};

Item.prototype.toggle_owner_validation = function() {
	this.set_comment_icon();
				
	var adv_valid = this.adviser_validation().is(':checked');
	var own_valid = this.owner_validation().is(':checked');
	
	if (adv_valid || own_valid) {
		this.dialog().children(".validation").slideDown();		
	} else {
		this.dialog().children(".validation").slideUp();
	}
	
	if (own_valid){
		this.owner_validation().attr('value', 'y');
		var stack = this.children.slice(0);
		while (stack.length > 0) {
			var tmp = stack.shift();
			tmp = Item.find(tmp);
			if (!tmp.owner_validation().is(':checked')) {
				tmp.owner_validation().attr('value', 'y');
				tmp.owner_validation().attr('checked', true).attr('readonly', true).attr('disabled', true);
				tmp.set_comment_icon();
				stack = stack.concat(tmp.children);
			}
		}
	} else {
		this.owner_validation().attr('value', 'i');
		var stack = this.children.slice(0);
		while (stack.length > 0) {
			var tmp = stack.shift();
			tmp = Item.find(tmp);
			if (tmp.owner_validation().is(':disabled')) {
				tmp.owner_validation().attr('value', 'n');
				tmp.owner_validation().removeAttr('checked').removeAttr('readonly').removeAttr('disabled');
				tmp.set_comment_icon();
				stack = stack.concat(tmp.children);
			}
		}
	}
};

Item.prototype.toggle_objective = function() {
	this.set_comment_icon();
	
	if (this.objective().is(':checked')){
		this.objective().attr('value', 'y');
		this.dialog().children(".objective").slideDown();
		
		var stack = this.children.slice(0);
		while (stack.length > 0) {
			var tmp = stack.shift();
			tmp = Item.find(tmp);
			if (!tmp.objective().is(':checked')) {
				tmp.objective().attr('value', 'i');
				tmp.objective().attr('checked', true).attr('readonly', true).attr('disabled', true);
				tmp.set_comment_icon();
				stack = stack.concat(tmp.children);
			}
		}
	} else {
		this.objective().attr('value', 'n');
		this.dialog().children(".objective").slideUp();
		
		var stack = this.children.slice(0);
		while (stack.length > 0) {
			var tmp = stack.shift();
			tmp = Item.find(tmp);
			if (tmp.objective().is(':disabled')) {
				tmp.objective().attr('value', 'n');
				tmp.objective().removeAttr('checked').removeAttr('readonly').removeAttr('disabled');
				tmp.set_comment_icon();
				stack = stack.concat(tmp.children);
			}
		}
	}
};

Item.prototype.set_comment_icon = function() {
	var icon = "/images/comment.png";
	if (this.objective().is(':checked')){
		icon = "/images/target.png";
	}
	if (this.owner_validation().is(':checked')){
		icon = "/images/progress.gif";
	}
	if (this.adviser_validation().is(':checked')){
		icon = "/images/confirm.png";
	}
	
	this.dialog_link().children('img').attr('src', icon);
};

Item.prototype.parent = function() {
	if (StringUtils.trimToNull(this.parent_key) == undefined)
		return undefined;
	else
		return Item.find(this.parent_key);
};

Item.prototype.root = function() {
	if (StringUtils.trimToNull(this.parent_key) == undefined) {
		return this;
	} else {
		return Item.find(this.parent_key).root();
	}
};

Item.prototype.label = function() {
	return $("#label_"+this.key);
};

Item.prototype.checkEndDate = function (d) {
	if (this.ends().attr('disabled') == true) {
		return true;
	}

	if (d.getTime() < this.start_date.getTime()) {
		throw new Exception("[" + this.label().text() + "]: La date de fin ne peut pas être antérieure à la date de début.");
	}
	
	if (this.parent() == null || this.parent() == undefined) {
		return true;
	}
	
	// Meme date que son parent, on peut modifier.
	if (d.getTime() == this.parent().end_date.getTime()) {
		return true;
	}
	
	return true;
};

Item.prototype.checkStartDate = function(d) {
	if (this.starts().attr('disabled') == true) {
		return true;
	}

	// On essaye de changer la date de début d'une étape. C'est interdit.
	if (StringUtils.trimToNull(this.parent_key) == undefined && Item.roots.indexOf(this.key) != 0) {
		throw new Exception("[" + this.label().text() + "]: Les dates de début des étapes ne peuvant pas être changées");
	}
	
	if (d.getTime() > this.end_date.getTime()) {
		throw new Exception("[" + this.label().text() + "]: La date de début ne peut pas être postérieure à la date de fin.");
	}
	
	if (this.parent() == null || this.parent() == undefined) {
		return true;
	}
	
	// Meme date que son parent, on peut modifier.
	if (d.getTime() == this.parent().start_date.getTime()) {
		return true;
	}
	
	// Un sous item ne peut pas commencer avant son parent.
	if (d.getTime() < this.root().start_date.getTime()) {
		throw new Exception("[" + this.label().text() + "]: Cet item ne peut pas commencer avant l'étape à laquelle il est rattaché.");
	}
	
	// on compte le nombre d'item qui ont la même date de début que le parent (sauf lui)
	var n = 0;
	for (var v in this.parent().children) {
		var child = Item.find(this.parent().children[v]);
		if (child == undefined || child == null) {
			console.error("Can't find item named " + this.parent().children[v]); 
		} else if (this != child && child.start_date.getTime() <= this.parent().start_date.getTime()) {
			n++;
		}
	}
	
	// si c'est le dernier, c'est pas bon
	if (n == 0) {
		throw new Exception("[" + this.label().text() + "]: Le dernier item qui a la même date de début que son parent ne peut être déplacé.");
	}
	
	return true;
};